package yolo

import (
	"appengine"
	"appengine/taskqueue"
	"appengine/urlfetch"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	chromiumStatusURL         = "http://chromium-status.appspot.com/allstatus?format=json"
	chromiumStatusTimeLayout  = "2006-01-02 15:04:05"
	ActivityChromiumTreeOpen  = "ChromiumTreeOpen"
	ActivityChromiumTreeClose = "ChromiumTreeClose"
)

var (
	autoCloserRegexp = regexp.MustCompile(`Tree is closed \(Automatic: .* on .* from [0-9]*: ([^\)]*)\)`)
)

type RawAction struct {
	Username          string `json: "username"`
	Date              string `json: "date"`
	Message           string `json: "message"`
	Can_Commit_Freely bool   `json: "can_commit_freely"`
	General_State     string `json: "general_state"`
}

type Action struct {
	Username          string
	Date              string
	Time              time.Time
	Message           string
	Can_Commit_Freely bool
	General_State     string
	Recipients        []string
}

func (a Action) String() string {
	var s []string
	s = append(s, a.Date)
	s = append(s, fmt.Sprintf("username: %s", a.Username))
	s = append(s, fmt.Sprintf("time: %s", a.Time.Format(chromiumStatusTimeLayout)))
	s = append(s, fmt.Sprintf("message: %s", a.Message))
	if a.Can_Commit_Freely {
		s = append(s, "can_commit_freely")
	}
	s = append(s, fmt.Sprintf("general_state: %s", a.General_State))
	if len(a.Recipients) > 0 {
		s = append(s, fmt.Sprintf("recipients: %v", a.Recipients))
	}
	return strings.Join(s, "\n")
}

type ChromiumStatus struct {
	*url.URL
	Actions []Action
}

func NewChromiumStatus(path string) *ChromiumStatus {
	statusUrl, _ := url.Parse(path)
	return &ChromiumStatus{URL: statusUrl}
}

func (cstatus *ChromiumStatus) fetch(c *http.Client) (err error) {
	resp, err := c.Get(cstatus.URL.String())
	if err != nil {
		return err
	}
	d := json.NewDecoder(resp.Body)
	var actions []RawAction
	err = d.Decode(&actions)
	for _, action := range actions {
		var a Action
		a.Username = action.Username
		a.Date = action.Date
		a.Time, _ = time.Parse(chromiumStatusTimeLayout, action.Date[:len(chromiumStatusTimeLayout)])
		a.Message = action.Message
		a.Can_Commit_Freely = action.Can_Commit_Freely
		a.General_State = action.General_State
		a.Recipients = append(a.Recipients, a.Username)
		if m := autoCloserRegexp.FindStringSubmatch(a.Message); m != nil {
			a.Recipients = append(a.Recipients, strings.Split(m[1], ", ")...)
		}
		cstatus.Actions = append(cstatus.Actions, a)
	}
	return nil
}

func postStartCheckChromiumStatus(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	_, err := postTask(c, "/worker/chromium_status_start_check", "default",
		PostParams{})
	if err != nil {
		c.Warningf("post err:%s", err)
		http.Error(w, fmt.Sprintf("Post error: %s", err.Error()), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Posted status_start_check request.")
}

func startCheckChromiumStatus(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	var last_timestamp int64
	startTime := r.FormValue("startTime")
	if startTime != "" {
		last_timestamp, _ = strconv.ParseInt(startTime, 10, 0)
	}
	timestamp, err := checkChromiumStatus(c, last_timestamp)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	if timestamp < last_timestamp {
		timestamp = last_timestamp
	}
	t := taskqueue.NewPOSTTask("/worker/chromium_status_start_check",
		map[string][]string{"startTime": {strconv.FormatInt(timestamp, 10)}})
	t.Name = fmt.Sprintf("check-chromium-status-%s", (time.Now().Format("2006-01-02T15-04")))
	t.Delay = 60 * 1000 * 1000 // 1 min
	_, err = taskqueue.Add(c, t, "default")
	if err != nil {
		c.Warningf("post err:%s", err)
	}
}

func checkChromiumStatus(c appengine.Context, last_timestamp int64) (timestamp int64, err error) {
	c.Debugf("check: %s %d", chromiumStatusURL, last_timestamp)
	param := ""
	if last_timestamp > 0 {
		param = fmt.Sprintf("&startTime=%d&endTime=%d",
			last_timestamp, time.Now().Unix())
	}
	chromiumStatus := NewChromiumStatus(chromiumStatusURL + param)
	err = chromiumStatus.fetch(urlfetch.Client(c))
	if err != nil {
		c.Warningf("fetch failed: %s", err)
		return -1, err
	}
	for _, action := range chromiumStatus.Actions {
		if action.Time.Unix() < last_timestamp-1 {
			c.Debugf("old action %s by %s @ %d", action.Message, action.Username, action.Time.Unix())
			continue
		}
		if timestamp < action.Time.Unix() {
			timestamp = action.Time.Unix()
		}
		a, _ := json.Marshal(action)
		c.Debugf("store %s by %s @ %d", action.Message, action.Username, action.Time.Unix())
		_, err = postTask(c, "/worker/chromium_status_store_action",
			"frequent",
			PostParams{"action": {string(a)}})
	}
	return timestamp, nil
}

func storeChromiumStatusAction(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	a := r.FormValue("action")
	var action Action
	err := json.Unmarshal([]byte(a), &action)
	if err != nil {
		c.Warningf("store unmarshal failed: %s", err)
		return
	}
	putChromiumStatusActivity(c, &action)
}

func putChromiumStatusActivity(c appengine.Context, action *Action) {
	var a ActivityData
	a.Activity.User = action.Username
	a.Activity.Time = action.Time
	a.Activity.URL = "http://chromium-status.appspot.com/status_viewer"
	a.Activity.SiteURL = "chromium-status.appspot.com"
	a.Activity.Snippet = action.Message
	if action.Can_Commit_Freely {
		a.Activity.Type = ActivityChromiumTreeOpen
	} else {
		a.Activity.Type = ActivityChromiumTreeClose
	}
	c.Debugf("store %s by %s", a.Activity.Type, a.Activity.User)
	if len(action.Recipients) > 0 {
		ar := &a.ActivityRecipients
		ar.Recipients = action.Recipients
	}
	RecordActivityData(c, &a)
}

func notifyChromiumTreeOpen(c appengine.Context, a *ActivityData) {
	if setNotificationGreetingMessage(c, a.Activity.Snippet, a.Activity.Time) {
		notifyMessage(c, a.Activity.Snippet)
	}
}

func notifyChromiumTreeClose(c appengine.Context, a *ActivityData) {
	if setNotificationGreetingMessage(c, a.Activity.Snippet, a.Activity.Time) {
		notifyMessage(c, a.Activity.Snippet)
	}
}
